#include	"lp_global_vars.h"

struct timeval startTime_Total, endTime_Total, end, start, performance_start_time, performance_end_time, last_proposal_time, tv1, tv2;
struct sockaddr_in *ipmAddr;
struct event *print_stat_timeout_eve, *timeoutEv2, *socketEv1, *socketEv2;

int learnerID, proposerID;
int lowestDelivered;

int SUBMIT_PER_WAKE, digit;

inst *data;
proposer_msg * p_msgs;
inst *new_data;
int is_proposer;

struct sockaddr_in server_addr;
const char * config_path;

//------------------------------------------------------------------------------
void initialize_events();
void initialize_buffers();
void validate_parameters(int argc, const char *argv[]);
void clean_exit(int sig);
void enable_ctrl_c_handler();

//------------------------------------------------------------------------------
int main(int argc, const char *argv[]) {
	enable_ctrl_c_handler();
	int status;

	initialize_events();
	validate_parameters(argc, argv);
	p_initialize();
	lp_config_parser(config_path, proposerID, learnerID, is_proposer);

	//Learner joins the group to which messages are multicast
	ipmAddr = GET_SOCKADDR();
	//==========================================================================
	initialize_buffers();
	printf("-----------------------------------------------------------\n");
	event_init();

	evtimer_set(print_stat_timeout_eve, compute_performance, print_stat_timeout_eve); //for both the learner and the proposer
	status = evtimer_add(print_stat_timeout_eve, &tv1);
	assert(status >= 0);

	event_set(socketEv1, get_config_info()->server_infos[get_config_info()->learner_id].multicast_recv_sid, EV_READ | EV_PERSIST, recv_new_packet, NULL);
	status = event_add(socketEv1, NULL);
	assert(status >= 0);

	event_set(socketEv2, get_config_info()->learner_infos[get_config_info()->learner_id].learner_to_acceptor_sid, EV_READ | EV_PERSIST, recv_lost_packet, NULL);
	status = event_add(socketEv2, NULL);
	assert(status >= 0);

	//==========================================================================
	if (is_proposer == 1) {
		printf("I am proposer: %d	%d\n", learnerID, proposerID);
		evtimer_set(timeoutEv2, generate_msg, timeoutEv2);
		status = evtimer_add(timeoutEv2, &tv2);
		assert(status >= 0);
	}
	//==========================================================================
	event_dispatch();

	close(get_config_info()->proposer_to_multicaster_sid);
	close(get_config_info()->learner_infos[learnerID].learner_to_acceptor_sid);
	exit(0);
}
//------------------------------------------------------------------------------
void initialize_events() {
	tv1.tv_sec = 1;
	tv1.tv_usec = 0;

	socketEv1 = (struct event*) malloc(sizeof(struct event));
	socketEv2 = (struct event*) malloc(sizeof(struct event));
	print_stat_timeout_eve = (struct event*) malloc(sizeof(struct event));
	timeoutEv2 = (struct event*) malloc(sizeof(struct event));
}
//------------------------------------------------------------------------------
void initialize_buffers() {

	lp_interval_performance = malloc(sizeof(lp_performance));
	lp_next_performance_index = 0;
	lp_nulify_performance(lp_interval_performance);

	int i;
	data = (inst*) malloc(sizeof(inst) * INTERNAL_BUFFER_SIZE);
	new_data = (inst*) malloc(sizeof(inst));
	p_msgs = (proposer_msg *) malloc(sizeof(proposer_msg) * INTERNAL_BUFFER_SIZE);
	lowestDelivered = -1;
	digit = 0;

	for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
		data[(i % INTERNAL_BUFFER_SIZE)].deliver = false;
		data[(i % INTERNAL_BUFFER_SIZE)].received = false;
		data[(i % INTERNAL_BUFFER_SIZE)].highestDeliverable = -1;
		data[(i % INTERNAL_BUFFER_SIZE)].packet.msgs[0].msg_id = 0;
	}

	gettimeofday(&start, NULL);
	gettimeofday(&startTime_Total, NULL);
	gettimeofday(&performance_start_time, NULL);
	gettimeofday(&last_proposal_time, NULL);
}
//------------------------------------------------------------------------------
void validate_parameters(int argc, const char *argv[]) {
	if (argc != 9) {
		printf("Incompatible call to this function. Try Again.!\n"
			"<1. learnerID>\n"
			"<2. proposerID>\n"
			"<3. Usec (Microseconds)>\n"
			"<4. Num of Packets per Interval>\n"
			"<5. Config Path>\n"
			"<6. Performance file name>\n"
			"<7. Latency File name>\n"
			"<8. Is proposer? (y=1/n=0)>\n");
		exit(1);
	} else {

		tv2.tv_sec = 0;
		learnerID = atoi(argv[1]);
		proposerID = atoi(argv[2]);
		tv2.tv_usec = atoi(argv[3]);
		SUBMIT_PER_WAKE = atoi(argv[4]);
		config_path = argv[5];
		lp_performance_file = argv[6];
		lp_latency_file = argv[7];
		is_proposer = atoi(argv[8]);
	}
}
void clean_exit(int sig) {
	signal(sig, clean_exit);
	printf("Caught exit signal\n");
	lp_store_performance_in_file(lp_performance_file, lp_latency_file, lp_next_performance_index);
	close(get_config_info()->proposer_to_multicaster_sid);
	close(get_config_info()->learner_infos[learnerID].learner_to_acceptor_sid);
	free(data);
	free(p_msgs);
	exit(0);
}
void enable_ctrl_c_handler() {
	signal(SIGINT, clean_exit);
	signal(SIGKILL, clean_exit);
	signal(SIGABRT, clean_exit);
	signal(SIGALRM, clean_exit);
	signal(SIGBUS, clean_exit);
	signal(SIGCHLD, clean_exit);
	signal(SIGCONT, clean_exit);
	signal(SIGFPE, clean_exit);
	signal(SIGHUP, clean_exit);
	signal(SIGILL, clean_exit);
	signal(SIGPIPE, clean_exit);
	signal(SIGQUIT, clean_exit);
	signal(SIGSEGV, clean_exit);
	signal(SIGSTOP, clean_exit);
	signal(SIGTERM, clean_exit);
	signal(SIGTSTP, clean_exit);
	signal(SIGTTIN, clean_exit);
	signal(SIGTTOU, clean_exit);
	signal(SIGUSR1, clean_exit);
	signal(SIGUSR2, clean_exit);
	signal(SIGPROF, clean_exit);
	signal(SIGSYS, clean_exit);
	signal(SIGTRAP, clean_exit);
	signal(SIGURG, clean_exit);
	signal(SIGVTALRM, clean_exit);
	signal(SIGXCPU, clean_exit);
	signal(SIGXFSZ, clean_exit);

}

